/**
 ****************************************************************************************
 *
 * @file lkm3_server.c
 *
 * @brief Server Implementation.
 *
 * Copyright (C) RivieraWaves 2009-2016
 *
 *
 ****************************************************************************************
 */

/**
 ****************************************************************************************
 * @addtogroup LKM3_SERVER
 * @{
 ****************************************************************************************
 */

/*
 * INCLUDE FILES
 ****************************************************************************************
 */

#include "rwip_config.h"
#include "lkm3_server.h"
#include "lkm3_server_task.h"
#include "prf_utils.h"
#include "prf.h"

#include "ke_mem.h"

/*
 * LKM3_SERVER ATTRIBUTES DEFINITION
 ****************************************************************************************
 */
/// Full LKM3_SERVER Database Description - Used to add attributes into the database
const struct attm_desc lkm3_server_att_db[LKM3_SERVER_IDX_NB] =
    {
        // Service Declaration
        [LKM3_SERVER_IDX_SVC] = {ATT_DECL_PRIMARY_SERVICE, PERM(RD, ENABLE), 0, 0},

        // Characteristic Declaration
        [LKM3_SERVER_IDX_DEMO_CHAR1] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0},
        // Characteristic Value
        [LKM3_SERVER_IDX_DEMO_VAL1] = {ATT_SVC_LKM3_SERVER_CHAC1, PERM(WRITE_REQ, ENABLE) | PERM(WRITE_COMMAND, ENABLE), 0, LKM3_SERVER_MAX_CHAC_LEN},

        // Characteristic Declaration
        [LKM3_SERVER_IDX_DEMO_CHAR2] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0},
        // Characteristic Value
        [LKM3_SERVER_IDX_DEMO_VAL2] = {ATT_SVC_LKM3_SERVER_CHAC2, PERM(NTF, ENABLE), 0, LKM3_SERVER_MAX_CHAC_LEN},
        // Characteristic - Client Characteristic Configuration Descriptor
        [LKM3_SERVER_IDX_DEMO_NTF_CFG] = {ATT_DESC_CLIENT_CHAR_CFG, PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE), 0, 0},

};

/*
 * LOCAL FUNCTION DEFINITIONS
 ****************************************************************************************
 */

/**
 ****************************************************************************************
 * @brief Initialization of the LKM3_SERVER module.
 * This function performs all the initializations of the Profile module.
 *  - Creation of database (if it's a service)
 *  - Allocation of profile required memory
 *  - Initialization of task descriptor to register application
 *      - Task State array
 *      - Number of tasks
 *      - Default task handler
 *
 * @param[out]    env        Collector or Service allocated environment data.
 * @param[in|out] start_hdl  Service start handle (0 - dynamically allocated), only applies for services.
 * @param[in]     app_task   Application task number.
 * @param[in]     sec_lvl    Security level (AUTH, EKS and MI field of @see enum attm_value_perm_mask)
 * @param[in]     param      Configuration parameters of profile collector or service (32 bits aligned)
 *
 * @return status code to know if profile initialization succeed or not.
 ****************************************************************************************
 */
static uint8_t lkm3_server_init(struct prf_task_env *env, uint16_t *start_hdl, uint16_t app_task, uint8_t sec_lvl, struct lkm3_server_db_cfg *params)
{
    uint16_t shdl;
    struct lkm3_server_env_tag *lkm3_server_env = NULL;
    // Status
    uint8_t status = GAP_ERR_NO_ERROR;

    //-------------------- allocate memory required for the profile  ---------------------
    lkm3_server_env = (struct lkm3_server_env_tag *)ke_malloc(sizeof(struct lkm3_server_env_tag), KE_MEM_ATT_DB);
    memset(lkm3_server_env, 0, sizeof(struct lkm3_server_env_tag));

    shdl = *start_hdl;

    // Create LKM3_SERVER in the DB
    //------------------ create the attribute database for the profile -------------------
    status = attm_svc_create_db(&shdl, ATT_SVC_LKM3_SERVER_SERVICE, NULL,
                                LKM3_SERVER_IDX_NB, NULL, env->task, lkm3_server_att_db,
                                ((sec_lvl & (PERM_MASK_SVC_DIS | PERM_MASK_SVC_AUTH | PERM_MASK_SVC_EKS))));

    //-------------------- Update profile task information  ---------------------
    if (status == ATT_ERR_NO_ERROR)
    {
        // allocate LKM3_SERVER required environment variable
        env->env = (prf_env_t *)lkm3_server_env;
        *start_hdl = shdl;
        lkm3_server_env->start_hdl = *start_hdl;
        lkm3_server_env->prf_env.app_task = app_task | (PERM_GET(sec_lvl, SVC_MI) ? PERM(PRF_MI, ENABLE) : PERM(PRF_MI, DISABLE));
        lkm3_server_env->prf_env.prf_task = env->task | PERM(PRF_MI, DISABLE);

        // initialize environment variable
        env->id = TASK_ID_LKM3_SERVER;
        lkm3_server_task_init(&(env->desc));

        // service is ready, go into an Idle state
        ke_state_set(env->task, LKM3_SERVER_IDLE);
    }
    else if (lkm3_server_env != NULL)
    {
        ke_free(lkm3_server_env);
    }

    return (status);
}

/**
 ****************************************************************************************
 * @brief Destruction of the LKM3_SERVER module - due to a reset for instance.
 * This function clean-up allocated memory (attribute database is destroyed by another
 * procedure)
 *
 * @param[in|out]    env        Collector or Service allocated environment data.
 ****************************************************************************************
 */
static void lkm3_server_destroy(struct prf_task_env *env)
{
    struct lkm3_server_env_tag *lkm3_server_env = (struct lkm3_server_env_tag *)env->env;

    // clear on-going operation
    if (lkm3_server_env->operation != NULL)
    {
        ke_free(lkm3_server_env->operation);
    }

    // free profile environment variables
    env->env = NULL;
    ke_free(lkm3_server_env);
}

/**
 ****************************************************************************************
 * @brief Handles Connection creation
 *
 * @param[in|out]    env        Collector or Service allocated environment data.
 * @param[in]        conidx     Connection index
 ****************************************************************************************
 */
static void lkm3_server_create(struct prf_task_env *env, uint8_t conidx)
{
    struct lkm3_server_env_tag *lkm3_server_env = (struct lkm3_server_env_tag *)env->env;
    ASSERT_ERR(conidx < BLE_CONNECTION_MAX);

    // force notification config to zero when peer device is connected
    lkm3_server_env->ntf_cfg[conidx] = 0;
}

/**
 ****************************************************************************************
 * @brief Handles Disconnection
 *
 * @param[in|out]    env        Collector or Service allocated environment data.
 * @param[in]        conidx     Connection index
 * @param[in]        reason     Detach reason
 ****************************************************************************************
 */
static void lkm3_server_cleanup(struct prf_task_env *env, uint8_t conidx, uint8_t reason)
{
    struct lkm3_server_env_tag *lkm3_server_env = (struct lkm3_server_env_tag *)env->env;

    ASSERT_ERR(conidx < BLE_CONNECTION_MAX);
    // force notification config to zero when peer device is disconnected
    lkm3_server_env->ntf_cfg[conidx] = 0;
}

/*
 * GLOBAL VARIABLE DEFINITIONS
 ****************************************************************************************
 */

/// LKM3_SERVER Task interface required by profile manager
const struct prf_task_cbs lkm3_server_itf =
    {
        (prf_init_fnct)lkm3_server_init,
        lkm3_server_destroy,
        lkm3_server_create,
        lkm3_server_cleanup,
};

/*
 * GLOBAL FUNCTIONS DEFINITIONS
 ****************************************************************************************
 */

const struct prf_task_cbs *lkm3_server_prf_itf_get(void)
{
    return &lkm3_server_itf;
}

uint16_t lkm3_server_get_att_handle(uint8_t att_idx)
{
    struct lkm3_server_env_tag *lkm3_server_env = PRF_ENV_GET(LKM3_SERVER, lkm3_server);
    uint16_t handle = lkm3_server_env->start_hdl + att_idx;
    return handle;
}

uint8_t lkm3_server_get_att_idx(uint16_t handle, uint8_t *att_idx)
{
    struct lkm3_server_env_tag *lkm3_server_env = PRF_ENV_GET(LKM3_SERVER, lkm3_server);
    *att_idx = handle - lkm3_server_env->start_hdl;
    return ATT_ERR_NO_ERROR;
}

/// @} LKM3_SERVER
